package niconama04.model.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.jdo.JDOObjectNotFoundException;
import javax.jdo.PersistenceManager;
import javax.jdo.Query;

import niconama04.shared.data.Category;
import niconama04.shared.data.CategoryOriginal;
import niconama04.shared.data.Constants;
import niconama04.shared.data.Constants.Pos;
import niconama04.shared.data.Grammer;
import niconama04.shared.data.GrammerWord;
import niconama04.shared.data.Original;
import niconama04.shared.data.OriginalUser;
import niconama04.shared.data.User;
import niconama04.shared.data.Word;

/**
 * Data Access Object
 * 
 * @author brigen
 */
class Dao {

	/** PersistenceManager */
	private PersistenceManager pm;

	/**
	 * [constructor]
	 *
	 * @param x_pm PersistenceManager
	 */
	Dao(PersistenceManager x_pm) {
		pm = x_pm;
	}

	/**
	 * 文法を返す
	 *
	 * @param x_key key
	 * @return 文法
	 */
	Grammer getGrammer(Long x_key) {
		Grammer entity;
		try {
			entity = pm.getObjectById(Grammer.class, x_key);
		} catch (JDOObjectNotFoundException e) {
			entity = null;
		}
		return entity;
	}

	/**
	 * 文法リストを返す
	 *
	 * @return 文法リスト
	 */
	List<Grammer> getGrammers() {
		return prv_getGrammers(null);
	}

	/**
	 * 文法リストを返す
	 *
	 * @param x_configuration 文法
	 * @return 文法リスト
	 * @throws IllegalArgumentException configurationがnullの場合
	 */
	List<Grammer> getGrammers(String x_configuration) {
		if (x_configuration == null) {
			throw new IllegalArgumentException("[x_configuration:String](argment at 0) is null.");
		}
		return prv_getGrammers(x_configuration);
	}

	/**
	 * 文法リストを返す
	 *
	 * @param x_configuration 文法
	 * @return 文法リスト
	 */
	private List<Grammer> prv_getGrammers(String x_configuration) {
		StringBuilder filter = new StringBuilder();
		StringBuilder parameters = new StringBuilder();
		Map<String, Object> map = new HashMap<String, Object>();
		Query query = pm.newQuery(Grammer.class);
		if (x_configuration != null) {
			if (0 < filter.length()) {
				filter.append(" && ");
			}
			if (0 < parameters.length()) {
				parameters.append(", ");
			}
			filter.append("configuration == x_configuration");
			parameters.append("String x_configuration");
			map.put("x_configuration", x_configuration);
		}
		if (0 < filter.length()) {
			query.setFilter(filter.toString());
		}
		if (0 < parameters.length()) {
			query.declareParameters(parameters.toString());
		}
		@SuppressWarnings("unchecked")
		List<Grammer> result = convert((List<Grammer>) (map.isEmpty() ? query.execute(): query.executeWithMap(map)));
		return result;
	}

	/**
	 * 文法を永続化する
	 *
	 * @param x_grammer 文法
	 */
	void registGrammer(Grammer x_grammer) {
		pm.makePersistent(x_grammer);
	}

	/**
	 * 単語を返す
	 *
	 * @param x_key key
	 * @return 単語
	 */
	Word getWord(Long x_key) {
		Word entity;
		try {
			entity = pm.getObjectById(Word.class, x_key);
			entity.setPos(Constants.getPos(entity.getPosKey()));
		} catch (JDOObjectNotFoundException e) {
			entity = null;
		}
		return entity;
	}

	/**
	 * 単語リストを返す
	 *
	 * @return 単語リスト
	 */
	List<Word> getWords() {
		return prv_getWords(null, null, null, null);
	}

	/**
	 * 単語リストを返す
	 *
	 * @param x_originalKey 原型key
	 * @return 単語リスト
	 */
	List<Word> getWords(Long x_originalKey) {
		return prv_getWords(x_originalKey, null, null, null);
	}

	/**
	 * 単語リストを返す
	 *
	 * @param x_posKey 品詞key
	 * @return 単語リスト
	 */
	List<Word> getWords(int x_posKey) {
		return prv_getWords(null, Integer.valueOf(x_posKey), null, null);
	}

	/**
	 * 単語リストを返す
	 *
	 * @param x_surface 表示
	 * @return 単語リスト
	 * @throws IllegalArgumentException surfaceがnullの場合
	 */
	List<Word> getWords(String x_surface) {
		if (x_surface == null) {
			throw new IllegalArgumentException("[x_surface:String](argment at 0) is null.");
		}
		return prv_getWords(null, null, x_surface, null);
	}

	/**
	 * 単語リストを返す
	 *
	 * @param x_posKey 品詞key
	 * @param x_surface 表示
	 * @return 単語リスト
	 * @throws IllegalArgumentException posまたはsurfaceがnullの場合
	 */
	List<Word> getWords(int x_posKey, String x_surface) {
		if (x_surface == null) {
			throw new IllegalArgumentException("[x_surface:String](argment at 1) is null.");
		}
		return prv_getWords(null, Integer.valueOf(x_posKey), x_surface, null);
	}

	/**
	 * 単語リストを返す
	 *
	 * @param x_posKey 品詞key
	 * @param x_surface 表示
	 * @param x_baseform 原形
	 * @return 単語リスト
	 * @throws IllegalArgumentException posまたはsurfaceまたはbaseformがnullの場合
	 */
	List<Word> getWords(int x_posKey, String x_surface, String x_baseform) {
		if (x_surface == null) {
			throw new IllegalArgumentException("[x_surface:String](argment at 1) is null.");
		}
		if (x_baseform == null) {
			throw new IllegalArgumentException("[x_baseform:String](argment at 2) is null.");
		}
		return prv_getWords(null, Integer.valueOf(x_posKey), x_surface, x_baseform);
	}

	/**
	 * 単語リストを返す
	 *
	 * @param x_originalKey
	 * @param x_posKey 品詞key
	 * @param x_surface 表示
	 * @param x_baseform 原形
	 * @return 単語リスト
	 */
	private List<Word> prv_getWords(Long x_originalKey, Integer x_posKey, String x_surface, String x_baseform) {
		StringBuilder filter = new StringBuilder();
		StringBuilder parameters = new StringBuilder();
		Map<String, Object> map = new HashMap<String, Object>();
		Query query = pm.newQuery(Word.class);
		if (x_originalKey != null) {
			if (0 < filter.length()) {
				filter.append(" && ");
			}
			if (0 < parameters.length()) {
				parameters.append(", ");
			}
			filter.append("originalKey == x_originalKey");
			parameters.append("Long x_originalKey");
			map.put("x_originalKey", x_originalKey);
		}
		if (x_posKey != null) {
			if (0 < filter.length()) {
				filter.append(" && ");
			}
			if (0 < parameters.length()) {
				parameters.append(", ");
			}
			filter.append("posKey == x_posKey");
			parameters.append("int x_posKey");
			map.put("x_posKey", x_posKey);
		}
		if (x_surface != null) {
			if (0 < filter.length()) {
				filter.append(" && ");
			}
			if (0 < parameters.length()) {
				parameters.append(", ");
			}
			filter.append("surface == x_surface");
			parameters.append("String x_surface");
			map.put("x_surface", x_surface);
		}
		if (x_baseform != null) {
			if (0 < filter.length()) {
				filter.append(" && ");
			}
			if (0 < parameters.length()) {
				parameters.append(", ");
			}
			filter.append("baseform == x_baseform");
			parameters.append("String x_baseform");
			map.put("x_baseform", x_baseform);
		}
		if (0 < filter.length()) {
			query.setFilter(filter.toString());
		}
		if (0 < parameters.length()) {
			query.declareParameters(parameters.toString());
		}
		@SuppressWarnings("unchecked")
		List<Word> result = convert((List<Word>) (map.isEmpty() ? query.execute(): query.executeWithMap(map)));
		for (Word w : result) {
			w.setPos(Constants.getPos(w.getPosKey()));
		}
		return result;
	}

	/**
	 * 単語を永続化する
	 *
	 * @param x_word 単語
	 */
	void registWord(Word x_word) {
		x_word.setPosKey(x_word.getPos().getKey());
		pm.makePersistent(x_word);
	}

	/**
	 * 文法-単語を返す
	 *
	 * @param x_key key
	 * @return 文法-単語
	 */
	GrammerWord getGrammerWord(Long x_key) {
		GrammerWord entity;
		try {
			entity = pm.getObjectById(GrammerWord.class, x_key);
		} catch (JDOObjectNotFoundException e) {
			entity = null;
		}
		return entity;
	}

	/**
	 * 文法-単語リストを返す
	 *
	 * @return 文法-単語リスト
	 */
	List<GrammerWord> getGrammerWords() {
		return prv_getGrammerWords(null, null, null);
	}

	/**
	 * 文法-単語リストを返す
	 *
	 * @param x_grammerKey 文法key
	 * @param x_wordKey 単語key
	 * @return 文法-単語リスト
	 * @throws IllegalArgumentException grammerKeyとwordKeyがともにnullの場合
	 */
	List<GrammerWord> getGrammerWords(Long x_grammerKey, Long x_wordKey) {
		if ((x_grammerKey == null) && (x_wordKey == null)) {
			throw new IllegalArgumentException("[x_grammerKey:Long](argment at 0) is null and [x_wordKey:Long](argment at 1) is null.");
		}
		return prv_getGrammerWords(x_grammerKey, x_wordKey, null);
	}

	/**
	 * 文法-単語リストを返す
	 *
	 * @param x_grammerKey 文法key
	 * @param x_wordKey 単語key
	 * @param x_index 位置
	 * @return 文法-単語リスト
	 * @throws IllegalArgumentException grammerKeyとwordKeyがともにnullの場合
	 */
	List<GrammerWord> getGrammerWords(Long x_grammerKey, Long x_wordKey, int x_index) {
		if ((x_grammerKey == null) && (x_wordKey == null)) {
			throw new IllegalArgumentException("[x_grammerKey:Long](argment at 0) is null and [x_wordKey:Long](argment at 1) is null.");
		}
		return prv_getGrammerWords(x_grammerKey, x_wordKey, Integer.valueOf(x_index));
	}

	private List<GrammerWord> prv_getGrammerWords(Long x_grammerKey, Long x_wordKey, Integer x_index) {
		StringBuilder filter = new StringBuilder();
		StringBuilder parameters = new StringBuilder();
		Map<String, Object> map = new HashMap<String, Object>();
		Query query = pm.newQuery(GrammerWord.class);
		if (x_grammerKey != null) {
			if (0 < filter.length()) {
				filter.append(" && ");
			}
			if (0 < parameters.length()) {
				parameters.append(", ");
			}
			filter.append("grammerKey == x_grammerKey");
			parameters.append("Long x_grammerKey");
			map.put("x_grammerKey", x_grammerKey);
		}
		if (x_wordKey != null) {
			if (0 < filter.length()) {
				filter.append(" && ");
			}
			if (0 < parameters.length()) {
				parameters.append(", ");
			}
			filter.append("wordKey == x_wordKey");
			parameters.append("Long x_wordKey");
			map.put("x_wordKey", x_wordKey);
		}
		if (x_index != null) {
			if (0 < filter.length()) {
				filter.append(" && ");
			}
			if (0 < parameters.length()) {
				parameters.append(", ");
			}
			filter.append("index == x_index");
			parameters.append("int x_index");
			map.put("x_index", x_index);
		}
		if (0 < filter.length()) {
			query.setFilter(filter.toString());
		}
		if (0 < parameters.length()) {
			query.declareParameters(parameters.toString());
		}
		@SuppressWarnings("unchecked")
		List<GrammerWord> result = convert((List<GrammerWord>) (map.isEmpty() ? query.execute(): query.executeWithMap(map)));
		return result;
	}

	/**
	 * 文法-単語を永続化する
	 *
	 * @param x_grammerWord 文法-単語
	 */
	void registGrammerWord(GrammerWord x_grammerWord) {
		pm.makePersistent(x_grammerWord);
	}

	/**
	 * 原型を返す
	 *
	 * @param x_key key
	 * @return 原型
	 */
	Original getOriginal(Long x_key) {
		Original entity;
		try {
			entity = pm.getObjectById(Original.class, x_key);
			entity.setPos(Constants.getPos(entity.getPosKey()));
		} catch (JDOObjectNotFoundException e) {
			entity = null;
		}
		return entity;
	}

	/**
	 * 原型リストを返す
	 *
	 * @return 原型リスト
	 */
	List<Original> getOriginals() {
		return prv_getOriginals(null, null);
	}

	/**
	 * 原型リストを返す
	 *
	 * @param x_pos 品詞
	 * @return 原型リスト
	 * @throws IllegalArgumentException posがnullの場合
	 */
	List<Original> getOriginals(Pos x_pos) {
		if (x_pos == null) {
			throw new IllegalArgumentException("[x_pos:Pos](argment at 0) is null.");
		}
		return prv_getOriginals(x_pos, null);
	}

	/**
	 * 原型リストを返す
	 *
	 * @param x_baseform 原形
	 * @return 原型リスト
	 * @throws IllegalArgumentException baseformがnullの場合
	 */
	List<Original> getOriginals(String x_baseform) {
		if (x_baseform == null) {
			throw new IllegalArgumentException("[x_baseform:String](argment at 0) is null.");
		}
		return prv_getOriginals(null, x_baseform);
	}

	/**
	 * 原型リストを返す
	 *
	 * @param x_pos 品詞
	 * @param x_baseform 原形
	 * @return 原型リスト
	 * @throws IllegalArgumentException posまたはbaseformがnullの場合
	 */
	List<Original> getOriginals(Pos x_pos, String x_baseform) {
		if (x_pos == null) {
			throw new IllegalArgumentException("[x_pos:Pos](argment at 0) is null.");
		}
		if (x_baseform == null) {
			throw new IllegalArgumentException("[x_baseform:String](argment at 1) is null.");
		}
		return prv_getOriginals(x_pos, x_baseform);
	}

	/**
	 * 原型リストを返す
	 *
	 * @param x_pos 品詞
	 * @param x_baseform 原形
	 * @return 原型リスト
	 */
	private List<Original> prv_getOriginals(Pos x_pos, String x_baseform) {
		StringBuilder filter = new StringBuilder();
		StringBuilder parameters = new StringBuilder();
		Map<String, Object> map = new HashMap<String, Object>();
		Query query = pm.newQuery(Original.class);
		if (x_pos != null) {
			if (0 < filter.length()) {
				filter.append(" && ");
			}
			if (0 < parameters.length()) {
				parameters.append(", ");
			}
			filter.append("posKey == x_posKey");
			parameters.append("int x_posKey");
			map.put("x_posKey", Integer.valueOf(x_pos.getKey()));
		}
		if (x_baseform != null) {
			if (0 < filter.length()) {
				filter.append(" && ");
			}
			if (0 < parameters.length()) {
				parameters.append(", ");
			}
			filter.append("baseform == x_baseform");
			parameters.append("String x_baseform");
			map.put("x_baseform", x_baseform);
		}
		if (0 < filter.length()) {
			query.setFilter(filter.toString());
		}
		if (0 < parameters.length()) {
			query.declareParameters(parameters.toString());
		}
		@SuppressWarnings("unchecked")
		List<Original> result = convert((List<Original>) (map.isEmpty() ? query.execute(): query.executeWithMap(map)));
		for (Original w : result) {
			w.setPos(Constants.getPos(w.getPosKey()));
		}
		return result;
	}

	/**
	 * 原型を永続化する
	 *
	 * @param x_original 原型
	 */
	void registOriginal(Original x_original) {
		x_original.setPosKey(x_original.getPos().getKey());
		pm.makePersistent(x_original);
	}

	/**
	 * カテゴリを返す
	 *
	 * @param x_key key
	 * @return カテゴリ
	 */
	Category getCategory(Long x_key) {
		Category entity;
		try {
			entity = pm.getObjectById(Category.class, x_key);
		} catch (JDOObjectNotFoundException e) {
			entity = null;
		}
		return entity;
	}

	/**
	 * カテゴリリストを返す
	 *
	 * @return カテゴリリスト
	 */
	List<Category> getCategorys() {
		return prv_getCategorys(null);
	}

	/**
	 * カテゴリリストを返す
	 *
	 * @param x_name カテゴリ名
	 * @return カテゴリリスト
	 * @throws IllegalArgumentException nameがnullの場合
	 */
	List<Category> getCategorys(String x_name) {
		if (x_name == null) {
			throw new IllegalArgumentException("[x_name:String](argment at 0) is null.");
		}
		return prv_getCategorys(x_name);
	}

	/**
	 * カテゴリリストを返す
	 *
	 * @param x_name カテゴリ名
	 * @return カテゴリリスト
	 */
	private List<Category> prv_getCategorys(String x_name) {
		StringBuilder filter = new StringBuilder();
		StringBuilder parameters = new StringBuilder();
		Map<String, Object> map = new HashMap<String, Object>();
		Query query = pm.newQuery(Category.class);
		if (x_name != null) {
			if (0 < filter.length()) {
				filter.append(" && ");
			}
			if (0 < parameters.length()) {
				parameters.append(", ");
			}
			filter.append("name == x_name");
			parameters.append("String x_name");
			map.put("x_name", x_name);
		}
		if (0 < filter.length()) {
			query.setFilter(filter.toString());
		}
		if (0 < parameters.length()) {
			query.declareParameters(parameters.toString());
		}
		@SuppressWarnings("unchecked")
		List<Category> result = convert((List<Category>) (map.isEmpty() ? query.execute(): query.executeWithMap(map)));
		return result;
	}

	/**
	 * カテゴリを永続化する
	 *
	 * @param x_category カテゴリ
	 */
	void registCategory(Category x_category) {
		pm.makePersistent(x_category);
	}

	/**
	 * カテゴリ-原型を返す
	 *
	 * @param x_key key
	 * @return カテゴリ-原型
	 */
	CategoryOriginal getCategoryOriginal(Long x_key) {
		CategoryOriginal entity;
		try {
			entity = pm.getObjectById(CategoryOriginal.class, x_key);
		} catch (JDOObjectNotFoundException e) {
			entity = null;
		}
		return entity;
	}

	/**
	 * カテゴリ-原型リストを返す
	 *
	 * @return カテゴリ-原型リスト
	 */
	List<CategoryOriginal> getCategoryOriginals() {
		return prv_getCategoryOriginals(null, null);
	}

	/**
	 * カテゴリ-原型リストを返す
	 *
	 * @param x_categoryKey カテゴリkey
	 * @param x_originalKey 原型key
	 * @return カテゴリ-原型リスト
	 * @throws IllegalArgumentException ategoryKeyとoriginalKeyが両方ともnullの場合
	 */
	List<CategoryOriginal> getCategoryOriginals(Long x_categoryKey, Long x_originalKey) {
		if ((x_categoryKey == null) && (x_originalKey == null)) {
			throw new IllegalArgumentException("[x_categoryKey:Long](argment at 0) is null and [x_originalKey:Long](argment at 1) is null.");
		}
		return prv_getCategoryOriginals(x_categoryKey, x_originalKey);
	}

	private List<CategoryOriginal> prv_getCategoryOriginals(Long x_categoryKey, Long x_originalKey) {
		StringBuilder filter = new StringBuilder();
		StringBuilder parameters = new StringBuilder();
		Map<String, Object> map = new HashMap<String, Object>();
		Query query = pm.newQuery(CategoryOriginal.class);
		if (x_categoryKey != null) {
			if (0 < filter.length()) {
				filter.append(" && ");
			}
			if (0 < parameters.length()) {
				parameters.append(", ");
			}
			filter.append("categoryKey == x_categoryKey");
			parameters.append("Long x_categoryKey");
			map.put("x_categoryKey", x_categoryKey);
		}
		if (x_originalKey != null) {
			if (0 < filter.length()) {
				filter.append(" && ");
			}
			if (0 < parameters.length()) {
				parameters.append(", ");
			}
			filter.append("originalKey == x_originalKey");
			parameters.append("Long x_originalKey");
			map.put("x_originalKey", x_originalKey);
		}
		if (0 < filter.length()) {
			query.setFilter(filter.toString());
		}
		if (0 < parameters.length()) {
			query.declareParameters(parameters.toString());
		}
		@SuppressWarnings("unchecked")
		List<CategoryOriginal> result = convert((List<CategoryOriginal>) (map.isEmpty() ? query.execute(): query.executeWithMap(map)));
		return result;
	}

	/**
	 * カテゴリ-原型を永続化する
	 *
	 * @param x_categoryOriginal カテゴリ-原型
	 */
	void registCategoryOriginal(CategoryOriginal x_categoryOriginal) {
		pm.makePersistent(x_categoryOriginal);
	}

	/**
	 * <p>
	 * カテゴリ-原型を削除する
	 * </p>
	 * <p>
	 * keyがnullでなければ、keyが一致するentityを削除する<br>
	 * keyがnullで、かつcategoryKeyとoriginalKeyがともにnullでなければ、categoryKeyとoriginalKeyの両方が一致するentityを削除する
	 * </p>
	 *
	 * @param x_categoryOriginal カテゴリ-原型
	 */
	void deleteCategoryOriginal(CategoryOriginal x_categoryOriginal) {
		CategoryOriginal co = null;
		Long key = x_categoryOriginal.getKey();
		if (key != null) {
			co = getCategoryOriginal(key);
		} else {
			Long categoryKey = x_categoryOriginal.getCategoryKey();
			Long originalKey = x_categoryOriginal.getOriginalKey();
			if ((categoryKey != null) && (originalKey != null)) {
				List<CategoryOriginal> cos = getCategoryOriginals(categoryKey, originalKey);
				if (!cos.isEmpty()) {
					co = cos.get(0);
				}
			}
			if (co != null) {
				pm.deletePersistent(co);
			}
		}
	}

	/**
	 * シリアライズ可能なリストに詰め替える
	 *
	 * @param <E>
	 * @param x_list リスト
	 * @return シリアライズ可能なリスト
	 */
	private static <E> List<E> convert(List<E> x_list) {
		List<Object> es = new ArrayList<Object>();
		for (E e : x_list) {
			es.add(e);
		}
		@SuppressWarnings("unchecked")
		List<E> result = (List<E>) es;
		return result;
	}



//------------------------------------------------------------------------------------------
// ここから以下は仮の実装で、動かしてすらいない
//   使うなよ、絶対に使うなよ
//-----------------------------------------------

	User getUser(Long x_key) {
		User entity;
		try {
			entity = pm.getObjectById(User.class, x_key);
		} catch (JDOObjectNotFoundException e) {
			entity = null;
		}
		return entity;
	}

	List<User> getUsers(String x_twitterId) {
		Query query = pm.newQuery(User.class);
		query.setFilter("twitterId == x_twitterId");
		query.declareParameters("String x_twitterId");
		@SuppressWarnings("unchecked")
		List<User> result = (List<User>) query.execute(x_twitterId);
		return result;
	}

	void registUser(User x_user) {
		pm.makePersistent(x_user);
	}

	OriginalUser getOriginalUser(Long x_key) {
		OriginalUser entity;
		try {
			entity = pm.getObjectById(OriginalUser.class, x_key);
		} catch (JDOObjectNotFoundException e) {
			entity = null;
		}
		return entity;
	}

	List<OriginalUser> getOriginalUsers(@SuppressWarnings("unused") Long x_wordKey, @SuppressWarnings("unused") Long x_userKey) {
		return new ArrayList<OriginalUser>();
	}

	void registOriginalUser(OriginalUser x_wordUser) {
		pm.makePersistent(x_wordUser);
	}
}
